﻿/**
* Fuse Kit 2
* Copyright (c) 2006 Moses Gunesch, MosesSupposes.com
* 
* Distributed under MIT Open Source License, see Fuse-Kit-License.html (in fuse package directory)
* Easing Equations (c) 2003 Robert Penner used by permission, see PennerEasing
* Visit http://www.mosessupposes.com/Fuse
*
* @ignore
*
* Shared class for common variables and error messages, also provides an easy way to extend the Kit for use with logging programs.
* @author	Moses Gunesch / MosesSupposes.com
* @version	2.1.3r1
*/
class com.mosesSupposes.fuse.FuseKitCommon {
	
	/**
	 * Enables kit version to be retrieved at runtime or when reviewing a decompiled swf. 
	 * @usage <pre>trace(FuseKitCommon.VERSION); // if the version is incorrect, clear your ASO cache.</pre>
	 */
	public static var VERSION:String = 'Fuse Kit 2.1.3r1 Copyright (c) 2006 Moses Gunesch, MosesSupposes.com under MIT Open Source License';
	
	/** Set to false for shortform (error-code-only) responses.
	 * @description
	 * Note that {@link com.mosesSupposes.fuse.ZigoEngine#OUTPUT_LEVEL} and {@link com.mosesSupposes.fuse.Fuse#OUTPUT_LEVEL} properties can be set to 0 for no output, 
	 * 1 for normal, 2 for additional info, and in Fuse 3 for additional FuseItem output.
	 * <br><br>To lower filesize check "omit trace actions" in Publish Settings on final publish (trace can also be removed from compile in MTASC).
	 */
	public static var VERBOSE:Boolean = true;
	
	/**
	 * To extend this class to use a logger, set this property to a custom method of your own.
	 * @description <code>FuseKitCommon.logOutput = function(s:String):Void { ... };</code>
	 */
	public static var logOutput:Function;
	
	
	/**
	 * Constant: String "ALL". Used with ZigoEngine in methods like removeTween, rewTween, etc.
	 */ 
	public static var ALL:String = 'ALL';
	
	/**
	 * Constant: String "ALLCOLOR". Used with ZigoEngine in methods like removeTween, rewTween, etc.
	 */ 
	public static var ALLCOLOR:String = 'ALLCOLOR';
	
	/**
	 * Internal counter used to tag each callback function with a unique id
	 */
	private static var cbTicker:Number = 0;
	
	// ----------- The rest is for use by the other classes in this kit. -----------------------------------------------------
	
	
	// Property collections used in various classes
	/**
	* @exclude
	* Internal use only. A shared variable that lists color keywords in a pipe-delimited string.
	* @return string
	*/
	public static function _cts():String { return '|_tint|_tintPercent|_brightness|_brightOffset|_contrast|_invertColor|_colorReset|_colorTransform|'; }
	/**
	* @exclude
	* Internal use only. A shared variable that lists color keywords in a pipe-delimited string.
	* @return string
	*/
	public static function _resetTo100():String { return '|_alpha|_contrast|_invertColor|_tintPercent|_xscale|_yscale|_scale|'; }
	/**
	* @exclude
	* Internal use only. A shared variable that lists color keywords in a pipe-delimited string.
	* @return string
	*/
	public static function _resetTo0():String { return '|_brightness|_brightOffset|_colorReset|_rotation|'; }
	/**
	* @exclude
	* Internal use only. A shared variable that lists auto-underscoreable action-object property keywords in a pipe-delimited string.
	* @return string
	*/
	public static function _underscoreable():String { return ((_cts())+'_frame|_x|_y|_xscale|_yscale|_scale|_width|_height|_size|_rotation|_alpha|_fade|_visible|'); }
	/**
	* @exclude
	* Internal use only. A shared variable that lists parseable ZigoEngine callback keywords in a pipe-delimited string.
	* @return string
	*/
	public static function _cbprops():String { return '|skipLevel|cycles|roundResults|extra1|extra2|func|scope|args|startfunc|startscope|startargs|updfunc|updscope|updargs|'; }
	/**
	* @exclude
	* Internal use only. A shared variable that lists standard Fuse play events in a pipe-delimited string.
	* @return string
	*/
	public static function _fuseEvents():String { return '|onStart|onStop|onPause|onResume|onAdvance|onComplete|'; }
	/**
	* @exclude
	* Internal use only. A shared variable that lists Fuse-specific action-object property keywords in a pipe-delimited string.
	* @return string
	*/
	public static function _fuseprops():String { return '|command|label|delay|event|eventparams|target|addTarget|trigger|startAt|ease|easing|seconds|duration|time|'; }
	/**
	* @exclude
	* Internal use only. Validates Fuse commands.
	* @return string
	*/
	public static function _validateFuseCommand(c:String, inGroup:Boolean, hasArg:Boolean, outputLevel:Number, simple:Boolean):Boolean { 
		var valid:Boolean = false;
		var misplaced:Boolean = false;
		switch(c){
			case 'start':
			case 'stop':
			case 'pause':
			case 'resume':
			case 'setStartProps':
				if (inGroup!=true) valid = true;
				break;
			case 'delay':
			case 'trigger':
				if (simple==true) {
					if (hasArg==true) valid = true; // allowed within groups in simple syntax
				} else {
					misplaced = true;
				} 
				inGroup = false;
				break;
			case 'skipTo':
				if (hasArg==true && inGroup!=true) valid = true;
		}
		if (outputLevel>0 && valid==false) error('109',c, inGroup, misplaced); 
		return valid;
	}
	
	/**
	* @exclude
	* Internal; left public to enable overwriting with a more custom method if needed for custom logging. Currently it simply either calls <code>trace</code> or <code>logOutput</code> with the message string based on whether <code>logOutput</code> has been user-defined.
	* @return string
	*/
	public static function output(s:String):Void
	{
		if (typeof logOutput=='function')	{
			logOutput(s);
		}	
		else {
			trace(s);
		}
	}
	
	/**
	* Internal: See this method in the class file for all normal output-level errors/warnings.
	* @description	Set {@link #VERBOSE} to false for shortform (error-code-only) messages.
	*/
	public static function error (errorCode:String):Void
	{
		// additional parameters that may be passed
		var a1:Object = arguments[1];
		var a2:Object = arguments[2];
		var a3:Object = arguments[3];
		if (VERBOSE!=true) {
			output('[FuseKitCommon#'+errorCode+']');
			return;	
		}
		var es:String = '';
		var _newline:String = '\n'; // using the regular newline constant doesn't work w/mtasc.
		
		switch (errorCode)
		{
			// 000 ZigoEngine
			case '001':	
				es+=	 '** ERROR: When using simpleSetup to extend prototypes, you must pass the Shortcuts class. **';
				es+=	 _newline+' import com.mosesSupposes.fuse.*;';
				es+=	 _newline+' ZigoEngine.simpleSetup(Shortcuts);'+_newline;
				break;
			case '002':
				es+= '** ZigoEngine.doShortcut: shortcuts missing. Use the setup commands: import com.mosesSupposes.fuse.*; ZigoEngine.register(Shortcuts); **';
				break;
			case '003':
				es+= _newline+'*** Error: DO NOT use #include "lmc_tween.as" with this version of ZigoEngine! ***'+_newline;
				break;
			case '004':
				es+= '** ZigoEngine.doTween - too few arguments ['+a1+'].';
				if (Boolean(a2)==true) {
					es += ' If you are trying to use Object Syntax without Fuse, pass FuseItem in your register() or simpleSetup() call. **';
				}
				else {
					es += ' Object syntax call failed. **';
				}
				break;
			case '005':
				es+= '** ZigoEngine.doTween - missing targets['+a1+'] and/or props['+a2+'] **';
				break;
			case '006':
				es+= '** Error: easing shortcut string not recognized ("'+a1+'"). You may need to pass the in PennerEasing class during register or simpleSetup. **';
				break;
			case '007':
				es+= '- ZigoEngine: Target locked ['+a1+'], ignoring tween call ['+a2+']';
				break;
			case '008':
				es+= '** You must register the Shortcuts class in order to use easy string-type callback parsing. **';
				break;
			case '009':
				es+= '** ZigoEngine: A callback parameter "'+a1+'" was not recognized. **';
				break;
			case '010':
				es+= '** '+((a1==true)?'ZigoEngine':'FuseItem')+' unable to parse '+((a2==1)?'callback[':String(a2)+' callbacks[')+a3+']. Try using the syntax {scope:this, func:"myFunction"} **';
				break;
			case '011':
				es+= '- ZigoEngine: Callbacks discarded via skipLevel 2 option ['+a1+'|'+a2+'].';
				break;
			case '012':
				es+= '- Engine set props or ignored no-change tween on: '+a1+', props passed:['+a2+'], endvals passed:['+a3+']';
				break;
			case '013':
				es+= '- Engine added tween on:\n\ttargets:['+a1+']\n\tprops:['+a2+']\n\tendvals:['+a3+']';
				break;
			case '014':
				es+= '** Error: easing function passed is not usable with this engine. Functions need to follow the Robert Penner model. **';
				break;
			case '015':
				es+= '** Error: The CustomEasing class must be passed during setup (register or simpleSetup) to use custom easing Arrays. **';
				break;
			case '016':
				es+= '[ ZigoEngine.TIME_MULTIPLIER: '+String(Number(Number(a1)*100))+'% ]';
				break;
			
			// 100 Fuse
			case '101':
				es+= '** ERROR: Fuse simpleSetup was removed in version 2.0! **';
				es+= _newline+' You must now use the following commands:';
				es+= _newline+_newline+'	import com.mosesSupposes.fuse.*;';
				es+= _newline+'	ZigoEngine.simpleSetup(Shortcuts, PennerEasing, Fuse);';
				es+= _newline+'Note that PennerEasing is optional, and FuseFMP is also accepted. (FuseFMP.simpleSetup is run automatically if included.)'+_newline;
				break;
			case '102':
				// a1 is 'skipTo' or 'fastForward'.
				es+= '** Fuse '+a1+' index or label not found ('+a2+') or out of range. **';
				break;
			case '103':
				es+= '** Fuse skipTo ('+a1+')  ignored - targets the current index ('+a2+'). **';
				break;
			case '104':
				es+= '** Fuse fastForward index out of play range ('+a1+') - skipTo has been called instead. **';
				break;
			case '105':
				es+= '** An unsupported Array method was called on Fuse. **';
				break;
			case '106': // Simple Syntax
				es+= '** ERROR: You have not set up Fuse correctly. **';
				es+= _newline+'You must now use the following commands (PennerEasing is optional).';
				es+= _newline+'	import com.mosesSupposes.fuse.*;';
				es+= _newline+'	ZigoEngine.simpleSetup(Shortcuts, PennerEasing, Fuse);'+_newline;
				break;
			case '107': // Simple Syntax
				es+= '** Fuse :: id not found - Aborting open(). **';
				break;
			case '108': // Simple Syntax
				es+= '** Fuse.startRecent: No recent Fuse found to start! **';
				break;
				break;
			case '109': // bad command
				es+= '** Command "'+a1+'" discarded. ';
				if (a2==true) es+= 'Not allowed within a group. **';
				else if (a3==true) es+= 'Not supported in Object Syntax, use the '+a1+' property instead. **';
				else es+= 'The command may be unrecognized or missing an argument. **';
				break;
			case '110':
				es += '** '+a1 + ' illegal Fuse property discarded:"'+a2+'". Bezier keywords other than x and y cannot be set as start values. **';
				break;
			case '112':
				es+= '** Fuse: missing com.mosesSupposes.fuse.ZigoEngine! Cannot tween. **';
				break;
			case '113': // bad callback
				es+= '** FuseItem: A callback has been discarded. Actions with a command may only contain: label, delay, scope, args. **';
				break;
//			case '114':
//				es+= '** FuseItem: command ("'+a1+'") discarded. Commands may not appear within action groups (arrays). **';
//				break;
			case '115':
				es+= a1+' overlapping prop discarded: '+a2;
				break;
			case '116':
				es+= '** FuseItem Error: Delays within groups (arrays) and start/update callbacks are not supported when using Fuse without ZigoEngine. Although you need to restructure your Fuse, it should be possible to achieve the same results. **'+_newline;
				break;
			case '117':
				es+= '** '+a1+': infinite cycles are not allowed within Fuses - discarded. **';
				break;
			case '118':
				es+= '** '+a1+': No targets found!'+((a2==true)?'  [Unable to set start props] **':'  [Skipping tween parameters in this action] **');
				break;
			case '119':
				es+= '** '+a1+': ' + ((a2==1)?'':a2+' actions in the group')+' missing targets'+((a3==true)?' during setStartProps **':' **');
				break;
			case '120':
				es+= '** '+a1+': conflict with "'+a2+'". Property might be doubled within a grouped-action array. **';
				break;
			case '121':
				es+= '** Fuse timecode formatting requires "00:" formatting (example:"01:01:33" yields 61.33 seconds.) **';
				break;
			case '122':
				es+= '** Event "'+a1+'" reserved by Fuse. **';
				break;
			case '123':
				es+= '** A Fuse event parameter failed in '+a1+' **';
				break;
			case '124':
				es+= '** '+a1+': trigger:'+a2+' ignored - only one trigger is allowed per action **';
				break;
			case '125':
				es+= a1+' Warning - fastForward hit an item during its tween cycle, may malfunction.';
				break;
			
			// 200 FuseFMP
			case '201':
				es+= '**** FuseFMP cannot initialize argument '+a1+' (BitmapFilters cannot be applied to this object type) ****';
				break;
			case '202':
				es+='** FuseFMP error: A '+a1+' could not be created for '+a2+' **';
				break;
			case '203':
				es+='** FuseFMP.setFilterProps - too few arguments passed ('+a1+') - minimum 2 required. **';
				break;
			case '204':
				es+='** FuseFMP.setFilterProps could not locate the filter passed. ('+a1+') **';
				break;

		}
		output(es);
	}
	
	/**
	 * @exclude
	 * Internal callback parser for ZigoEngine and FuseItem.
	 * User may pass easyfunc-string if Shortcuts class is registered, loose function as function or string, or callback object.
	 * @param callback		Parameter as passed by user to doTween or doShortcut
	 * @param targets		Tween targets, used in locating functions
	 * @param outputLevel	Output level of engine or fuse
	 * @param callerIsEngine FuseItem sends false
	 * @param addprefix		Used in FuseItem callback-not-found error since FuseItem calls this method once for each st/upd/end.
	 * @return				A formatted object containing properties skipLevel,cycles, and possibly extra1,extra2,start,upd,end
	 * 						The start, upd, end properties are sub-objects containing {s:scope, f:func, a:args}.
	 */
	public static function parseCallback(callback:Object, targets:Array, outputLevel:Number, callerIsEngine:Boolean, addprefix:String):Object
	{
		if (callback._vcb==true) return callback;
		var validCBs:Object = { _vcb:true, skipLevel:_global.com.mosesSupposes.fuse.ZigoEngine.SKIP_LEVEL, cycles:1 };
		if (callback==undefined) {
			return validCBs;
		}
		var cbErrors:Array = [];
		if (typeof callback=='object') {
			if (callback.skipLevel!=undefined && typeof callback.skipLevel=='number' && callback.skipLevel!=_global.com.mosesSupposes.fuse.ZigoEngine.SKIP_LEVEL) {
				if (callback.skipLevel>=0 && callback.skipLevel<=2) validCBs.skipLevel = callback.skipLevel;
			};
			if (callback.cycles!=undefined) {
				if (typeof callback.cycles=='number' && callback.cycles>-1) validCBs.cycles = callback.cycles;
				else if ((callback.cycles.toUpperCase())=='LOOP') validCBs.cycles = 0; // 0 is infinite loop flag.
			}
			if (callback.extra1!=undefined) validCBs.extra1 = callback.extra1;
			if (callback.extra2!=undefined) validCBs.extra2 = callback.extra2;
			if (callback.roundResults===true || callback.roundResults===false) {
				validCBs.roundResults = callback.roundResults;
			}
		}
		else {
			// plain string or function is interpreted as tween-end callback.
			callback = {func:callback};
		}
		// interpret easyfunc strings
		var prefixes:Array = ['start','upd',''];
		var easyfuncparse:Function = _global.com.mosesSupposes.fuse.Shortcuts.parseStringTypeCallback;
		for (var i:String in prefixes) {
			var prefix:String = prefixes[i];
			var fstr:String = callback[prefix+'func'];
			if (fstr!=undefined && typeof fstr=='string' && fstr.indexOf("(")>-1) {
				if (easyfuncparse!=undefined) {
					var efc:Object = easyfuncparse(fstr);
					if (efc.func!=undefined) {
						callback[prefix+'scope'] = efc.scope;
						callback[prefix+'func'] = efc.func;
						callback[prefix+'args'] = efc.args;
					}
				}
				else if (outputLevel>0) {
					error('008');
				}
			}
		}
		
		// now parse callback object for engine, or throw errors if not parseable.
		var basescope:Object = callback.scope;
		for (var i:String in callback) { // for-in enables detection of missing funcs that are declared in an object. a missing func passed in straight for callback is not detectable since it's undefined.
			var fi:Number = (i.toLowerCase()).indexOf('func');
			if (fi>-1) {
				var prefix:String = i.slice(0,fi);
				var func:Object = callback[i];
				var args:Object = callback[prefix+'args'];
				var scope:Object = (callback[prefix+'scope']==undefined) ? basescope : callback[prefix+'scope'];
				// try to locate an unscoped or mis-scoped func-string - in any target/_parent,_root,_global,finally via eval.
				if (typeof func=='string' && scope[func]==undefined) { 
					for (var j:String in targets) {
						var targ:Object = targets[j];
						if (typeof targ[func]=='function') {
							scope = targ;
							break;
						}
						if (typeof targ._parent[func]=='function') {
							scope = targ._parent;
							break;
						}
					}
					if (scope==undefined && _level0[func]!=undefined) scope = _level0;
					if (scope==undefined && _global[func]!=undefined) scope = _global;
				}
				if (typeof func!='function') {
					if (typeof scope[String(func)]=='function') func = scope[String(func)];
					else func = eval(String(func));
				}
				if (func==undefined) {
					cbErrors.push(String(((addprefix==null)?i:addprefix+i)+':'+((typeof callback[i]=='string')?'"'+callback[i]+'"':callback[i])+'/'+prefix+'scope:'+(scope)));
				}
				else {
					if (args!=undefined && !(args instanceof Array)) args = [args];
					if (prefix=='') prefix = 'end';
					validCBs[prefix] = {s:scope, f:func, a:args};
					if (callerIsEngine==true) validCBs[prefix].id = ++cbTicker;
					if (prefix=='start') validCBs.start.fired = false;
				}
			}
			else if ((_cbprops()).indexOf('|'+i+'|')==-1) {
				error('009',i);
			}
		}
		if (cbErrors.length>0 && outputLevel>0) {
			if (outputLevel>0) error('010',callerIsEngine, cbErrors.length, cbErrors.toString());
		}
		return validCBs;
	}
}